namespace Crux.Core.Websockets;
/// <summary>
/// GroupList
/// </summary>
internal class GroupList :
    IReadOnlyCollection<ConcurrentDictionary<string, WebSocketConnectionContext>>,
    IDisposableObservable
{
    /// <summary>
    ///
    /// </summary>
    /// <returns></returns>
    private readonly ConcurrentDictionary<string, GroupConnectionList> _groups = new ConcurrentDictionary<string, GroupConnectionList>(StringComparer.Ordinal);
    /// <summary>
    ///
    /// </summary>
    /// <returns></returns>
    private static readonly GroupConnectionList EmptyGroupConnectionList = new GroupConnectionList();

    /// <summary>
    ///
    /// </summary>
    public int Count => _groups.Count;
    /// <summary>
    ///
    /// </summary>
    /// <value></value>
    public bool IsDisposed { get; private set; }
    /// <summary>
    ///
    /// </summary>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public GroupList(CancellationToken cancellationToken = default)
    {
        cancellationToken.Register(() => Dispose());
    }
    /// <summary>
    ///
    /// </summary>
    /// <value></value>
    public ConcurrentDictionary<string, WebSocketConnectionContext>? this[string groupName]
    {
        get
        {
            _groups.TryGetValue(groupName, out var group);
            return group;
        }
    }
    /// <summary>
    ///
    /// </summary>
    /// <param name="connection"></param>
    /// <param name="groupName"></param>
    public void Add(WebSocketConnectionContext connection, string groupName)
    {
        CreateOrUpdateGroupWithConnection(groupName, connection);
    }
    /// <summary>
    ///
    /// </summary>
    /// <param name="connectionId"></param>
    /// <param name="groupName"></param>
    public void Remove(string connectionId, string groupName)
    {
        if (!_groups.TryGetValue(groupName, out var connections))
            return;

        if (!connections.TryRemove(connectionId, out var _) || !connections.IsEmpty)
            return;

        var groupToRemove = new KeyValuePair<string, GroupConnectionList>(groupName, EmptyGroupConnectionList);

        ((ICollection<KeyValuePair<string, GroupConnectionList>>)_groups).Remove(groupToRemove);
    }
    /// <summary>
    ///
    /// </summary>
    /// <param name="connectionId"></param>
    public void RemoveDisconnectedConnection(string connectionId)
    {
        var groupNames = _groups.Where(x => x.Value.Keys.Contains(connectionId)).Select(x => x.Key);

        foreach (var groupName in groupNames)
        {
            Remove(connectionId, groupName);
        }
    }
    /// <summary>
    ///
    /// </summary>
    /// <returns></returns>
    public IEnumerator<ConcurrentDictionary<string, WebSocketConnectionContext>> GetEnumerator() => _groups.Values.GetEnumerator();
    /// <summary>
    ///
    /// </summary>
    /// <returns></returns>
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    /// <summary>
    ///
    /// </summary>
    /// <param name="groupName"></param>
    /// <param name="connection"></param>
    private void CreateOrUpdateGroupWithConnection(string groupName, WebSocketConnectionContext connection)
    => _groups.AddOrUpdate(groupName, _ => AddConnectionToGroup(connection, new GroupConnectionList()),
            (key, oldCollection) =>
            {
                AddConnectionToGroup(connection, oldCollection);
                return oldCollection;
            });
    /// <summary>
    ///
    /// </summary>
    /// <param name="connection"></param>
    /// <param name="group"></param>
    /// <returns></returns>
    private static GroupConnectionList AddConnectionToGroup(
        WebSocketConnectionContext connection, GroupConnectionList group)
    {
        group.AddOrUpdate(connection.ConnectionId, connection, (_, __) => connection);
        return group;
    }
    /// <summary>
    ///
    /// </summary>
    public void Dispose()
    {
        if (this.IsDisposed) return;

        this.IsDisposed = true;
        GC.SuppressFinalize(this);
        _groups.Clear();
    }
}
/// <summary>
///
/// </summary>
internal class GroupConnectionList : ConcurrentDictionary<string, WebSocketConnectionContext>
{
    /// <summary>
    ///
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public override bool Equals(object? obj)
    {
        if (obj is ConcurrentDictionary<string, WebSocketConnectionContext> list)
        {
            return list.Count == Count;
        }
        return false;
    }
    /// <summary>
    ///
    /// </summary>
    /// <returns></returns>
    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
}
